package com.clei.utils;

import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * OkHttp 工具类
 *
 * @author KIyA
 */
public final class OkHttpUtil {

    /**
     * 问号
     */
    private static final char QUESTION_MARK = '?';

    /**
     * 等号
     */
    private static final char EQUALS_SIGN = '=';

    /**
     * &
     */
    private static final char AND = '&';

    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    /**
     * post json
     *
     * @param url     url
     * @param param   param
     * @param headers headers
     * @return String
     */
    public static String doPostJson(String url, String param, Map<String, String> headers) {
        PrintUtil.log("请求url : {}, param : {}, headers : {}", url, param, headers);
        return doRequest(url, getJsonBody(param), headers);
    }

    /**
     * post json
     *
     * @param url     url
     * @param param   param
     * @param headers headers
     * @return String
     */
    public static String doPostJson(String url, Object param, Map<String, String> headers) {
        String json = JsonUtil.toJson(param);
        PrintUtil.log("请求url : {}, param : {}, headers : {}", url, param, headers);
        return doRequest(url, getJsonBody(json), headers);
    }

    /**
     * post form
     *
     * @param url     url
     * @param param   param
     * @param headers headers
     * @return String
     */
    public static String doPostForm(String url, Map<String, Object> param, Map<String, String> headers) {
        return doRequest(url, getFormBody(param), headers);
    }

    private OkHttpUtil() {
    }

    /**
     * Get 请求
     *
     * @param url url
     * @return String
     */
    public static String doGet(String url) {
        return doRequest(url, null, null);
    }

    /**
     * Get 请求
     *
     * @param url     url
     * @param charset 指定返回字符串的字符集
     * @return String
     */
    public static String doGet(String url, Charset charset) {
        return doRequest(url, null, null, charset);
    }

    /**
     * post form
     *
     * @param url     url
     * @param field   field
     * @param value   value
     * @param headers headers
     * @return String
     */
    public static String doPostForm(String url, String field, Object value, Map<String, String> headers) {
        Map<String, Object> param = new HashMap<>(1);
        param.put(field, value);
        return doRequest(url, getFormBody(param), headers);
    }

    /**
     * Get 请求
     *
     * @param url   url
     * @param field field
     * @param value value
     * @return String
     */
    public static String doGet(String url, String field, Object value) {
        Map<String, Object> param = new HashMap<>(1);
        param.put(field, value);
        return doGet(url, param);
    }

    /**
     * Get 请求
     *
     * @param url   url
     * @param param param
     * @return String
     */
    public static String doGet(String url, Map<String, Object> param) {
        return doGet(url, null, param, null);
    }

    /**
     * Get 请求
     *
     * @param url     url
     * @param headers headers
     * @param param   param
     * @return String
     */
    public static String doGet(String url, Map<String, String> headers, Map<String, Object> param) {
        return doGet(url, headers, param, null);
    }

    /**
     * Get 请求
     *
     * @param url     url
     * @param headers headers
     * @param param   param
     * @param charset charset
     * @return String
     */
    public static String doGet(String url, Map<String, String> headers, Map<String, Object> param, Charset charset) {
        try {
            url = getUrl(url, param);
        } catch (Exception e) {
            PrintUtil.log("请求[{}]拼接参数时出错，参数值：{}", url, param);
            return null;
        }
        return null == charset ? doRequest(url, null, headers) : doRequest(url, null, headers, charset);
    }

    /**
     * downloadGet
     *
     * @param url url
     * @return InputStream
     */
    public static InputStream downloadGetStream(String url) {
        Request.Builder builder = new Request.Builder().url(url).get();
        Request request = builder.build();
        try {
            Response response = new OkHttpClient().newCall(request).execute();
            ResponseBody body = response.body();
            return body.byteStream();
        } catch (Exception e) {
            PrintUtil.log("请求[{}]出错", url, e);
        }
        return null;
    }

    /**
     * downloadGet
     *
     * @param url url
     * @return InputStream
     */
    public static byte[] downloadGetBytes(String url) {
        Request.Builder builder = new Request.Builder().url(url).get();
        Request request = builder.build();
        try {
            Response response = new OkHttpClient().newCall(request).execute();
            ResponseBody body = response.body();
            return body.bytes();
        } catch (Exception e) {
            PrintUtil.log("请求[{}]出错", url, e);
        }
        return null;
    }

    /**
     * get url
     *
     * @param url   url
     * @param param param
     * @return String
     * @throws java.io.UnsupportedEncodingException 不支持的编码
     */
    private static String getUrl(String url, Map<String, Object> param) throws UnsupportedEncodingException {
        if (CollectionUtil.isEmpty(param)) {
            return url;
        }
        StringBuilder sb = new StringBuilder(url);
        sb.append(QUESTION_MARK);
        for (Map.Entry<String, Object> entry : param.entrySet()) {
            sb.append(entry.getKey());
            sb.append(EQUALS_SIGN);
            Object v = entry.getValue();
            sb.append(URLEncoder.encode(null == v ? "" : String.valueOf(v), DEFAULT_CHARSET.name()));
            sb.append(AND);
        }
        // 最后一个字符处理
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }

    /**
     * json body
     *
     * @param param param
     * @return RequestBody
     */
    private static RequestBody getJsonBody(String param) {
        return RequestBody.create(param, MediaType.parse("application/json; charset=utf-8"));
    }

    /**
     * form body
     *
     * @param param param
     * @return RequestBody
     */
    private static RequestBody getFormBody(Map<String, Object> param) {
        FormBody.Builder builder = new FormBody.Builder();
        param.forEach((k, v) -> builder.add(k, String.valueOf(v)));
        return builder.build();
    }

    /**
     * multipart body
     *
     * @param file file
     * @return RequestBody
     */
    private static RequestBody getFormBody(File file) {
        return RequestBody.create(file, MediaType.parse("application/octet-stream"));
    }

    /**
     * multipart body
     *
     * @param map   fileMap
     * @param param param
     * @return RequestBody
     */
    private static RequestBody getMultipartBody(Map<String, File> map, Map<String, Object> param) {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        map.forEach((k, v) -> builder.addFormDataPart(k, v.getName(), getFormBody(v)));
        builder.addPart(getFormBody(param));
        return builder.build();
    }

    /**
     * Get / Post 请求
     *
     * @param url         url
     * @param requestBody requestBody
     * @param headers     headers
     * @param charsets    指定返回字符串使用的字符集
     * @return String
     */
    private static String doRequest(String url, RequestBody requestBody, Map<String, String> headers, Charset... charsets) {
        Request.Builder builder = new Request.Builder().url(url);
        if (!CollectionUtil.isEmpty(headers)) {
            builder = builder.headers(Headers.of(headers));
        }
        // null GET
        builder = null == requestBody ? builder.get() : builder.post(requestBody);
        Request request = builder.build();
        Response response;
        try {
            response = new OkHttpClient().newCall(request).execute();
        } catch (Exception e) {
            PrintUtil.log("请求[{}]出错", url, e);
            return null;
        }
        // 响应判断
        String result = null;
        if (response.isSuccessful()) {
            ResponseBody body = response.body();
            try {
                if (null != body) {
                    result = charsets.length > 0 ? new String(body.bytes(), charsets[0]) : body.string();
                }
            } catch (IOException e) {
                PrintUtil.log("", e);
            }
            PrintUtil.log("请求[{}]成功，结果：{}", url, result);
        } else {
            PrintUtil.log("请求[{}]失败，code：{}", url, response.code());
        }
        return result;
    }
}
